﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FormulParserNamespace
{
    public delegate double eval(params double[] operands);

    public abstract class Operation
    {
        protected string symbol;
        protected int priority;
        protected int operandsCount;

        public abstract double evaluate(params double[] operands);
        
        public int getOperandsCount()
        {
            return operandsCount;
        }

        public string getSymbol()
        {
            return string.Copy(symbol);
        }

        public static bool operator ==(Operation op1, Operation op2)
        {
            if (op1.getSymbol().Equals(op2.getSymbol()) ) return true;
            else return false;
        }

        public static bool operator !=(Operation op1, Operation op2)
        {
            return !(op1 == op2); 
        }

        public override bool Equals(object ob)
        {
            return this == (Operation)ob;
        }
    }

    public class InvalidOperandsCount : System.ApplicationException
    {
        public InvalidOperandsCount() : base() {}
        public InvalidOperandsCount(string str) : base(str) { }

        public override string ToString()
        {
            return Message;
        }
    }

    public class LeftBracket : Operation
    {
        public LeftBracket()
        {
            symbol = "(";
            operandsCount = 0;
            priority = 1;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \"(\" need 0 operands but get " + operands.Length);
            else return operands[0] + operands[1];
        }
    }

    public class Addition : Operation
    {
        public Addition()
        {
            symbol = "+";
            operandsCount = 2;
            priority = 2;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount( 
                    "operaiton \"+\" need 2 operands but get " + operands.Length );
            else return operands[0] + operands[1];
        }
    }

    public class Substraction : Operation
    {
        public Substraction()
        {
            symbol = "-";
            operandsCount = 2;
            priority = 2;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \"-\" need 2 operands but get " + operands.Length);
            else return operands[0] - operands[1];
        }
    }

    public class Multiplication : Operation
    {
        public Multiplication()
        {
            symbol = "*";
            operandsCount = 2;
            priority = 3;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \"*\" need " + getOperandsCount() + " operands but get " + operands.Length);
            else return operands[0] * operands[1];
        }
    }

    public class Division : Operation
    {
        public Division()
        {
            symbol = "/";
            operandsCount = 2;
            priority = 3;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \" " + getSymbol() + " \" need " + getOperandsCount() + 
                    " operands but get " + operands.Length);
            else return operands[0] / operands[1];
        }
    }

    public class Involution : Operation
    {
        public Involution()
        {
            symbol = "^";
            operandsCount = 2;
            priority = 4;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \" " + getSymbol() + " \" need " + getOperandsCount() +
                    " operands but get " + operands.Length);
            else return Math.Pow( operands[0], operands[1]);
        }
    }

    public class Sinus : Operation
    {
        public Sinus()
        {
            symbol = "sin";
            operandsCount = 1;
            priority = 5;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \" " + getSymbol() + " \" need " + getOperandsCount() +
                    " operands but get " + operands.Length);
            else return Math.Sin( operands[0] );
        }
    }

    public class Cosinus : Operation
    {
        public Cosinus()
        {
            symbol = "cos";
            operandsCount = 1;
            priority = 5;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \" " + getSymbol() + " \" need " + getOperandsCount() +
                    " operands but get " + operands.Length);
            else return Math.Cos(operands[0]);
        }
    }

    public class NaturalLogarithm : Operation
    {
        public NaturalLogarithm()
        {
            symbol = "ln";
            operandsCount = 1;
            priority = 5;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \" " + getSymbol() + " \" need " + getOperandsCount() +
                    " operands but get " + operands.Length);
            else return Math.Log(operands[0]);
        }
    }

    public class Exponent : Operation
    {
        public Exponent()
        {
            symbol = "exp";
            operandsCount = 1;
            priority = 5;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \" " + getSymbol() + " \" need " + getOperandsCount() +
                    " operands but get " + operands.Length);
            else return Math.Exp(operands[0]);
        }
    }

    public class DynamicCreateOperation : Operation
    {
        eval evaluationFunction;

        public DynamicCreateOperation(
            string _symbol, 
            int _priority,
            int _operandsCount,
            eval _evaluationFunction
            )
        {
            symbol = string.Copy(_symbol);
            priority = _priority;
            operandsCount = _operandsCount;
            evaluationFunction = _evaluationFunction;
        }

        public override double evaluate(params double[] operands)
        {
            if (operandsCount != operands.Length) throw new InvalidOperandsCount(
                    "operaiton \" " + getSymbol() + " \" need " + getOperandsCount() +
                    " operands but get " + operands.Length);
            else return evaluationFunction(operands);
        }
    }
}
